為什麼useReducer,reducer詞彙解釋—用流程圖解釋useReducer
2022-11-09 Wed
本文章內容涵蓋以下
- 為什麼你需要useReducer
- 關於reducer詞彙
- useReducer流程圖
- useReducer使用方式
為什麼需要useReducer
useReducer與useState有相似之處,允許客製化state的邏輯部份。如果想要管理複雜的狀態邏輯,useReducer可以幫助你達到關注點分離(separate the concerns),將其分成渲染和狀態管理,換句話說,也就是在component檔案僅負責渲染UI和分派事件,而複雜的邏輯計算管理狀態的部分可以再抽離出成另個檔案。
關於reducer詞彙
從原生的Javascript的Array method就有reduce的方法,如果將reduce輸入到字典翻譯的話,reducer有把...歸納、把...折合(成較小單位),在數學的用詞解釋有簡化、約簡,所以再回頭看Javascript的Array method也是將整個陣列經過reducer函式後回傳成單一值。
useReducer流程圖
我們再來看React的useReducer就可以嘗試著解釋成將各種複雜的邏輯歸納。
整個流程會如下
- component令事件觸發夾帶Action Object參數呼叫dispatch函式
- dispatch函式發送Action Object給Reducer
- Reducer處理如何更新state的邏輯後回傳State
- State最後使Component重新渲染

useReducer使用方式
這裡將分成以下部分講解
- useReducer
- reducer函式
- dispatch
- dispatch夾帶payload
useReducer hook
useReducer主要接受兩個參數
- reducer(簡化器、歸納器):換言之將狀態給歸納整理的一個函式
- initialState(初始狀態):來自於react的state。
如果嘗試著印出useReducer()的話,最後useReducer會回傳一個陣列 陣列的索引值0是state,索引值1是dispatch
console.log(useReducer());
這裡由於未帶入初始值,因此索引值0是undefined
reducer函式
這邊要作為useReducer的參數reducer函式宣告方式也需要兩個參數。
- state:來自於react的state
- action:將不同的行為分門別類
action 通常為一個物件例如
{type:'increment'}這邊引用官方的範例講解
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}我們要改變計數器的狀態,其中行為分成decrement(增加)、increment(減少),因此使用switch case來將行為分門別類,當收到的action是decrement的時候,將做某事(此範例是將原先數字加一),同理action接收到increment將數字減一。
另外這邊的default意思是當不是上述參數的時候丟出一個例外狀況。
dispatch
有了reducer函式後接下來要處理觸發時機,dispatch解釋成發送,下面範例中我們將dispatch參數帶入一個物件,屬性擁有type,值為decrement和increment做為reducer函式的action的值
function Counter() {
const initialState = {count: 0};
const [state, dispatch] = useReducer(reducer, initialState);
//這邊省略了reducer函式
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}dispatch發送夾帶payload
我們也可以在dispatch的時候夾帶參數,記得在reducer的時候將參數取出作為action行為觸發的時候帶入要處理的邏輯部分。
function Counter() {
const [number, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {number.count}
<button onClick={() => dispatch({ type: 'decrement'})}>-</button>
<button onClick={() => dispatch({ type: 'increment'})}>+</button>
</>
);
}惰性初始化(Lazy initialization)
這部分要講解的內容與先前文章從jQuery到VirtualDOM來淺談什麼是state—useState完整範例與介紹的useState類似可以進行lazy initialState。
當初始狀態是需要經過複雜的計算的function的時候,可以透過lazy initialzer帶入到useReducer的第三個參數,概念大致如下
function init(initialState){
// 很多複雜的程式碼
// 很多複雜的程式碼
// 很多複雜的程式碼
//經過複雜的計算後
return initialState
}
function Counter() {
const [number, dispatch] = useReducer(reducer, initialState,init);
return (
<>
Count: {number.count}
<button onClick={() => dispatch({ type: 'decrement'})}>-</button>
<button onClick={() => dispatch({ type: 'increment'})}>+</button>
</>
);
}透過init這個函式可以讓下次re-render的時候避免再次消耗計算效能,另外如果建立初始狀態的init函式不需要任何引數計算的話,可以將useReducer的第二個參數設為null 如下
const [state, dispatch] = useReducer(reducer, null,init)希望以上內容有幫助到大家,也歡迎留言如果有錯誤也歡迎糾正。
參考資料
An Easy Guide to React useReducer() Hook React useReducer Hook ultimate guide 官方useReducer介紹 reactBeta—Avoiding recreating the initial state